/*
 * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package javax.swing.text;

import java.awt.*;
import java.util.Vector;
import javax.swing.event.*;
import javax.swing.SizeRequirements;

/**
 * A View that tries to flow it's children into some
 * partially constrained space.  This can be used to
 * build things like paragraphs, pages, etc.  The
 * flow is made up of the following pieces of functionality.
 * <ul>
 * <li>A logical set of child views, which as used as a
 * layout pool from which a physical view is formed.
 * <li>A strategy for translating the logical view to
 * a physical (flowed) view.
 * <li>Constraints for the strategy to work against.
 * <li>A physical structure, that represents the flow.
 * The children of this view are where the pieces of
 * of the logical views are placed to create the flow.
 * </ul>
 *
 * @author Timothy Prinzing
 * @see View
 * @since 1.3
 */
public abstract class FlowView extends BoxView {

  /**
   * Constructs a FlowView for the given element.
   *
   * @param elem the element that this view is responsible for
   * @param axis may be either View.X_AXIS or View.Y_AXIS
   */
  public FlowView(Element elem, int axis) {
    super(elem, axis);
    layoutSpan = Integer.MAX_VALUE;
    strategy = new FlowStrategy();
  }

  /**
   * Fetches the axis along which views should be
   * flowed.  By default, this will be the axis
   * orthogonal to the axis along which the flow
   * rows are tiled (the axis of the default flow
   * rows themselves).  This is typically used
   * by the <code>FlowStrategy</code>.
   */
  public int getFlowAxis() {
    if (getAxis() == Y_AXIS) {
      return X_AXIS;
    }
    return Y_AXIS;
  }

  /**
   * Fetch the constraining span to flow against for
   * the given child index.  This is called by the
   * FlowStrategy while it is updating the flow.
   * A flow can be shaped by providing different values
   * for the row constraints.  By default, the entire
   * span inside of the insets along the flow axis
   * is returned.
   *
   * @param index the index of the row being updated. This should be a value &gt;= 0 and &lt;
   * getViewCount().
   * @see #getFlowStart
   */
  public int getFlowSpan(int index) {
    return layoutSpan;
  }

  /**
   * Fetch the location along the flow axis that the
   * flow span will start at.  This is called by the
   * FlowStrategy while it is updating the flow.
   * A flow can be shaped by providing different values
   * for the row constraints.
   *
   * @param index the index of the row being updated. This should be a value &gt;= 0 and &lt;
   * getViewCount().
   * @see #getFlowSpan
   */
  public int getFlowStart(int index) {
    return 0;
  }

  /**
   * Create a View that should be used to hold a
   * a rows worth of children in a flow.  This is
   * called by the FlowStrategy when new children
   * are added or removed (i.e. rows are added or
   * removed) in the process of updating the flow.
   */
  protected abstract View createRow();

  // ---- BoxView methods -------------------------------------

  /**
   * Loads all of the children to initialize the view.
   * This is called by the <code>setParent</code> method.
   * This is reimplemented to not load any children directly
   * (as they are created in the process of formatting).
   * If the layoutPool variable is null, an instance of
   * LogicalView is created to represent the logical view
   * that is used in the process of formatting.
   *
   * @param f the view factory
   */
  protected void loadChildren(ViewFactory f) {
    if (layoutPool == null) {
      layoutPool = new LogicalView(getElement());
    }
    layoutPool.setParent(this);

    // This synthetic insertUpdate call gives the strategy a chance
    // to initialize.
    strategy.insertUpdate(this, null, null);
  }

  /**
   * Fetches the child view index representing the given position in
   * the model.
   *
   * @param pos the position &gt;= 0
   * @return index of the view representing the given position, or -1 if no view represents that
   * position
   */
  protected int getViewIndexAtPosition(int pos) {
    if (pos >= getStartOffset() && (pos < getEndOffset())) {
      for (int counter = 0; counter < getViewCount(); counter++) {
        View v = getView(counter);
        if (pos >= v.getStartOffset() &&
            pos < v.getEndOffset()) {
          return counter;
        }
      }
    }
    return -1;
  }

  /**
   * Lays out the children.  If the span along the flow
   * axis has changed, layout is marked as invalid which
   * which will cause the superclass behavior to recalculate
   * the layout along the box axis.  The FlowStrategy.layout
   * method will be called to rebuild the flow rows as
   * appropriate.  If the height of this view changes
   * (determined by the preferred size along the box axis),
   * a preferenceChanged is called.  Following all of that,
   * the normal box layout of the superclass is performed.
   *
   * @param width the width to lay out against &gt;= 0.  This is the width inside of the inset
   * area.
   * @param height the height to lay out against &gt;= 0 This is the height inside of the inset
   * area.
   */
  protected void layout(int width, int height) {
    final int faxis = getFlowAxis();
    int newSpan;
    if (faxis == X_AXIS) {
      newSpan = width;
    } else {
      newSpan = height;
    }
    if (layoutSpan != newSpan) {
      layoutChanged(faxis);
      layoutChanged(getAxis());
      layoutSpan = newSpan;
    }

    // repair the flow if necessary
    if (!isLayoutValid(faxis)) {
      final int heightAxis = getAxis();
      int oldFlowHeight = (heightAxis == X_AXIS) ? getWidth() : getHeight();
      strategy.layout(this);
      int newFlowHeight = (int) getPreferredSpan(heightAxis);
      if (oldFlowHeight != newFlowHeight) {
        View p = getParent();
        if (p != null) {
          p.preferenceChanged(this, (heightAxis == X_AXIS), (heightAxis == Y_AXIS));
        }

        // PENDING(shannonh)
        // Temporary fix for 4250847
        // Can be removed when TraversalContext is added
        Component host = getContainer();
        if (host != null) {
          //nb idk 12/12/2001 host should not be equal to null. We need to add assertion here
          host.repaint();
        }
      }
    }

    super.layout(width, height);
  }

  /**
   * Calculate requirements along the minor axis.  This
   * is implemented to forward the request to the logical
   * view by calling getMinimumSpan, getPreferredSpan, and
   * getMaximumSpan on it.
   */
  protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
    if (r == null) {
      r = new SizeRequirements();
    }
    float pref = layoutPool.getPreferredSpan(axis);
    float min = layoutPool.getMinimumSpan(axis);
    // Don't include insets, Box.getXXXSpan will include them.
    r.minimum = (int) min;
    r.preferred = Math.max(r.minimum, (int) pref);
    r.maximum = Integer.MAX_VALUE;
    r.alignment = 0.5f;
    return r;
  }

  // ---- View methods ----------------------------------------------------

  /**
   * Gives notification that something was inserted into the document
   * in a location that this view is responsible for.
   *
   * @param changes the change information from the associated document
   * @param a the current allocation of the view
   * @param f the factory to use to rebuild if the view has children
   * @see View#insertUpdate
   */
  public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
    layoutPool.insertUpdate(changes, a, f);
    strategy.insertUpdate(this, changes, getInsideAllocation(a));
  }

  /**
   * Gives notification that something was removed from the document
   * in a location that this view is responsible for.
   *
   * @param changes the change information from the associated document
   * @param a the current allocation of the view
   * @param f the factory to use to rebuild if the view has children
   * @see View#removeUpdate
   */
  public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
    layoutPool.removeUpdate(changes, a, f);
    strategy.removeUpdate(this, changes, getInsideAllocation(a));
  }

  /**
   * Gives notification from the document that attributes were changed
   * in a location that this view is responsible for.
   *
   * @param changes the change information from the associated document
   * @param a the current allocation of the view
   * @param f the factory to use to rebuild if the view has children
   * @see View#changedUpdate
   */
  public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
    layoutPool.changedUpdate(changes, a, f);
    strategy.changedUpdate(this, changes, getInsideAllocation(a));
  }

  /**
   * {@inheritDoc}
   */
  public void setParent(View parent) {
    super.setParent(parent);
    if (parent == null
        && layoutPool != null) {
      layoutPool.setParent(null);
    }
  }

  // --- variables -----------------------------------------------

  /**
   * Default constraint against which the flow is
   * created against.
   */
  protected int layoutSpan;

  /**
   * These are the views that represent the child elements
   * of the element this view represents (The logical view
   * to translate to a physical view).  These are not
   * directly children of this view.  These are either
   * placed into the rows directly or used for the purpose
   * of breaking into smaller chunks, to form the physical
   * view.
   */
  protected View layoutPool;

  /**
   * The behavior for keeping the flow updated.  By
   * default this is a singleton shared by all instances
   * of FlowView (FlowStrategy is stateless).  Subclasses
   * can create an alternative strategy, which might keep
   * state.
   */
  protected FlowStrategy strategy;

  /**
   * Strategy for maintaining the physical form
   * of the flow.  The default implementation is
   * completely stateless, and recalculates the
   * entire flow if the layout is invalid on the
   * given FlowView.  Alternative strategies can
   * be implemented by subclassing, and might
   * perform incremental repair to the layout
   * or alternative breaking behavior.
   *
   * @since 1.3
   */
  public static class FlowStrategy {

    Position damageStart = null;
    Vector<View> viewBuffer;

    void addDamage(FlowView fv, int offset) {
      if (offset >= fv.getStartOffset() && offset < fv.getEndOffset()) {
        if (damageStart == null || offset < damageStart.getOffset()) {
          try {
            damageStart = fv.getDocument().createPosition(offset);
          } catch (BadLocationException e) {
            // shouldn't happen since offset is inside view bounds
            assert (false);
          }
        }
      }
    }

    void unsetDamage() {
      damageStart = null;
    }

    /**
     * Gives notification that something was inserted into the document
     * in a location that the given flow view is responsible for.  The
     * strategy should update the appropriate changed region (which
     * depends upon the strategy used for repair).
     *
     * @param e the change information from the associated document
     * @param alloc the current allocation of the view inside of the insets. This value will be null
     * if the view has not yet been displayed.
     * @see View#insertUpdate
     */
    public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
      // FlowView.loadChildren() makes a synthetic call into this,
      // passing null as e
      if (e != null) {
        addDamage(fv, e.getOffset());
      }

      if (alloc != null) {
        Component host = fv.getContainer();
        if (host != null) {
          host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
        }
      } else {
        fv.preferenceChanged(null, true, true);
      }
    }

    /**
     * Gives notification that something was removed from the document
     * in a location that the given flow view is responsible for.
     *
     * @param e the change information from the associated document
     * @param alloc the current allocation of the view inside of the insets.
     * @see View#removeUpdate
     */
    public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
      addDamage(fv, e.getOffset());
      if (alloc != null) {
        Component host = fv.getContainer();
        if (host != null) {
          host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
        }
      } else {
        fv.preferenceChanged(null, true, true);
      }
    }

    /**
     * Gives notification from the document that attributes were changed
     * in a location that this view is responsible for.
     *
     * @param fv the <code>FlowView</code> containing the changes
     * @param e the <code>DocumentEvent</code> describing the changes done to the Document
     * @param alloc Bounds of the View
     * @see View#changedUpdate
     */
    public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
      addDamage(fv, e.getOffset());
      if (alloc != null) {
        Component host = fv.getContainer();
        if (host != null) {
          host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
        }
      } else {
        fv.preferenceChanged(null, true, true);
      }
    }

    /**
     * This method gives flow strategies access to the logical
     * view of the FlowView.
     */
    protected View getLogicalView(FlowView fv) {
      return fv.layoutPool;
    }

    /**
     * Update the flow on the given FlowView.  By default, this causes
     * all of the rows (child views) to be rebuilt to match the given
     * constraints for each row.  This is called by a FlowView.layout
     * to update the child views in the flow.
     *
     * @param fv the view to reflow
     */
    public void layout(FlowView fv) {
      View pool = getLogicalView(fv);
      int rowIndex, p0;
      int p1 = fv.getEndOffset();

      if (fv.majorAllocValid) {
        if (damageStart == null) {
          return;
        }
        // In some cases there's no view at position damageStart, so
        // step back and search again. See 6452106 for details.
        int offset = damageStart.getOffset();
        while ((rowIndex = fv.getViewIndexAtPosition(offset)) < 0) {
          offset--;
        }
        if (rowIndex > 0) {
          rowIndex--;
        }
        p0 = fv.getView(rowIndex).getStartOffset();
      } else {
        rowIndex = 0;
        p0 = fv.getStartOffset();
      }
      reparentViews(pool, p0);

      viewBuffer = new Vector<View>(10, 10);
      int rowCount = fv.getViewCount();
      while (p0 < p1) {
        View row;
        if (rowIndex >= rowCount) {
          row = fv.createRow();
          fv.append(row);
        } else {
          row = fv.getView(rowIndex);
        }
        p0 = layoutRow(fv, rowIndex, p0);
        rowIndex++;
      }
      viewBuffer = null;

      if (rowIndex < rowCount) {
        fv.replace(rowIndex, rowCount - rowIndex, null);
      }
      unsetDamage();
    }

    /**
     * Creates a row of views that will fit within the
     * layout span of the row.  This is called by the layout method.
     * This is implemented to fill the row by repeatedly calling
     * the createView method until the available span has been
     * exhausted, a forced break was encountered, or the createView
     * method returned null.  If the remaining span was exhausted,
     * the adjustRow method will be called to perform adjustments
     * to the row to try and make it fit into the given span.
     *
     * @param rowIndex the index of the row to fill in with views.  The row is assumed to be empty
     * on entry.
     * @param pos The current position in the children of this views element from which to start.
     * @return the position to start the next row
     */
    protected int layoutRow(FlowView fv, int rowIndex, int pos) {
      View row = fv.getView(rowIndex);
      float x = fv.getFlowStart(rowIndex);
      float spanLeft = fv.getFlowSpan(rowIndex);
      int end = fv.getEndOffset();
      TabExpander te = (fv instanceof TabExpander) ? (TabExpander) fv : null;
      final int flowAxis = fv.getFlowAxis();

      int breakWeight = BadBreakWeight;
      float breakX = 0f;
      float breakSpan = 0f;
      int breakIndex = -1;
      int n = 0;

      viewBuffer.clear();
      while (pos < end && spanLeft >= 0) {
        View v = createView(fv, pos, (int) spanLeft, rowIndex);
        if (v == null) {
          break;
        }

        int bw = v.getBreakWeight(flowAxis, x, spanLeft);
        if (bw >= ForcedBreakWeight) {
          View w = v.breakView(flowAxis, pos, x, spanLeft);
          if (w != null) {
            viewBuffer.add(w);
          } else if (n == 0) {
            // if the view does not break, and it is the only view
            // in a row, use the whole view
            viewBuffer.add(v);
          }
          break;
        } else if (bw >= breakWeight && bw > BadBreakWeight) {
          breakWeight = bw;
          breakX = x;
          breakSpan = spanLeft;
          breakIndex = n;
        }

        float chunkSpan;
        if (flowAxis == X_AXIS && v instanceof TabableView) {
          chunkSpan = ((TabableView) v).getTabbedSpan(x, te);
        } else {
          chunkSpan = v.getPreferredSpan(flowAxis);
        }

        if (chunkSpan > spanLeft && breakIndex >= 0) {
          // row is too long, and we may break
          if (breakIndex < n) {
            v = viewBuffer.get(breakIndex);
          }
          for (int i = n - 1; i >= breakIndex; i--) {
            viewBuffer.remove(i);
          }
          v = v.breakView(flowAxis, v.getStartOffset(), breakX, breakSpan);
        }

        spanLeft -= chunkSpan;
        x += chunkSpan;
        viewBuffer.add(v);
        pos = v.getEndOffset();
        n++;
      }

      View[] views = new View[viewBuffer.size()];
      viewBuffer.toArray(views);
      row.replace(0, row.getViewCount(), views);
      return (views.length > 0 ? row.getEndOffset() : pos);
    }

    /**
     * Adjusts the given row if possible to fit within the
     * layout span.  By default this will try to find the
     * highest break weight possible nearest the end of
     * the row.  If a forced break is encountered, the
     * break will be positioned there.
     *
     * @param rowIndex the row to adjust to the current layout span.
     * @param desiredSpan the current layout span &gt;= 0
     * @param x the location r starts at.
     */
    protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) {
      final int flowAxis = fv.getFlowAxis();
      View r = fv.getView(rowIndex);
      int n = r.getViewCount();
      int span = 0;
      int bestWeight = BadBreakWeight;
      int bestSpan = 0;
      int bestIndex = -1;
      View v;
      for (int i = 0; i < n; i++) {
        v = r.getView(i);
        int spanLeft = desiredSpan - span;

        int w = v.getBreakWeight(flowAxis, x + span, spanLeft);
        if ((w >= bestWeight) && (w > BadBreakWeight)) {
          bestWeight = w;
          bestIndex = i;
          bestSpan = span;
          if (w >= ForcedBreakWeight) {
            // it's a forced break, so there is
            // no point in searching further.
            break;
          }
        }
        span += v.getPreferredSpan(flowAxis);
      }
      if (bestIndex < 0) {
        // there is nothing that can be broken, leave
        // it in it's current state.
        return;
      }

      // Break the best candidate view, and patch up the row.
      int spanLeft = desiredSpan - bestSpan;
      v = r.getView(bestIndex);
      v = v.breakView(flowAxis, v.getStartOffset(), x + bestSpan, spanLeft);
      View[] va = new View[1];
      va[0] = v;
      View lv = getLogicalView(fv);
      int p0 = r.getView(bestIndex).getStartOffset();
      int p1 = r.getEndOffset();
      for (int i = 0; i < lv.getViewCount(); i++) {
        View tmpView = lv.getView(i);
        if (tmpView.getEndOffset() > p1) {
          break;
        }
        if (tmpView.getStartOffset() >= p0) {
          tmpView.setParent(lv);
        }
      }
      r.replace(bestIndex, n - bestIndex, va);
    }

    void reparentViews(View pool, int startPos) {
      int n = pool.getViewIndex(startPos, Position.Bias.Forward);
      if (n >= 0) {
        for (int i = n; i < pool.getViewCount(); i++) {
          pool.getView(i).setParent(pool);
        }
      }
    }

    /**
     * Creates a view that can be used to represent the current piece
     * of the flow.  This can be either an entire view from the
     * logical view, or a fragment of the logical view.
     *
     * @param fv the view holding the flow
     * @param startOffset the start location for the view being created
     * @param spanLeft the about of span left to fill in the row
     * @param rowIndex the row the view will be placed into
     */
    protected View createView(FlowView fv, int startOffset, int spanLeft, int rowIndex) {
      // Get the child view that contains the given starting position
      View lv = getLogicalView(fv);
      int childIndex = lv.getViewIndex(startOffset, Position.Bias.Forward);
      View v = lv.getView(childIndex);
      if (startOffset == v.getStartOffset()) {
        // return the entire view
        return v;
      }

      // return a fragment.
      v = v.createFragment(startOffset, v.getEndOffset());
      return v;
    }
  }

  /**
   * This class can be used to represent a logical view for
   * a flow.  It keeps the children updated to reflect the state
   * of the model, gives the logical child views access to the
   * view hierarchy, and calculates a preferred span.  It doesn't
   * do any rendering, layout, or model/view translation.
   */
  static class LogicalView extends CompositeView {

    LogicalView(Element elem) {
      super(elem);
    }

    protected int getViewIndexAtPosition(int pos) {
      Element elem = getElement();
      if (elem.isLeaf()) {
        return 0;
      }
      return super.getViewIndexAtPosition(pos);
    }

    protected void loadChildren(ViewFactory f) {
      Element elem = getElement();
      if (elem.isLeaf()) {
        View v = new LabelView(elem);
        append(v);
      } else {
        super.loadChildren(f);
      }
    }

    /**
     * Fetches the attributes to use when rendering.  This view
     * isn't directly responsible for an element so it returns
     * the outer classes attributes.
     */
    public AttributeSet getAttributes() {
      View p = getParent();
      return (p != null) ? p.getAttributes() : null;
    }

    /**
     * Determines the preferred span for this view along an
     * axis.
     *
     * @param axis may be either View.X_AXIS or View.Y_AXIS
     * @return the span the view would like to be rendered into. Typically the view is told to
     * render into the span that is returned, although there is no guarantee. The parent may choose
     * to resize or break the view.
     * @see View#getPreferredSpan
     */
    public float getPreferredSpan(int axis) {
      float maxpref = 0;
      float pref = 0;
      int n = getViewCount();
      for (int i = 0; i < n; i++) {
        View v = getView(i);
        pref += v.getPreferredSpan(axis);
        if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) >= ForcedBreakWeight) {
          maxpref = Math.max(maxpref, pref);
          pref = 0;
        }
      }
      maxpref = Math.max(maxpref, pref);
      return maxpref;
    }

    /**
     * Determines the minimum span for this view along an
     * axis.  The is implemented to find the minimum unbreakable
     * span.
     *
     * @param axis may be either View.X_AXIS or View.Y_AXIS
     * @return the span the view would like to be rendered into. Typically the view is told to
     * render into the span that is returned, although there is no guarantee. The parent may choose
     * to resize or break the view.
     * @see View#getPreferredSpan
     */
    public float getMinimumSpan(int axis) {
      float maxmin = 0;
      float min = 0;
      boolean nowrap = false;
      int n = getViewCount();
      for (int i = 0; i < n; i++) {
        View v = getView(i);
        if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) == BadBreakWeight) {
          min += v.getPreferredSpan(axis);
          nowrap = true;
        } else if (nowrap) {
          maxmin = Math.max(min, maxmin);
          nowrap = false;
          min = 0;
        }
        if (v instanceof ComponentView) {
          maxmin = Math.max(maxmin, v.getMinimumSpan(axis));
        }
      }
      maxmin = Math.max(maxmin, min);
      return maxmin;
    }

    /**
     * Forward the DocumentEvent to the given child view.  This
     * is implemented to reparent the child to the logical view
     * (the children may have been parented by a row in the flow
     * if they fit without breaking) and then execute the superclass
     * behavior.
     *
     * @param v the child view to forward the event to.
     * @param e the change information from the associated document
     * @param a the current allocation of the view
     * @param f the factory to use to rebuild if the view has children
     * @see #forwardUpdate
     * @since 1.3
     */
    protected void forwardUpdateToView(View v, DocumentEvent e,
        Shape a, ViewFactory f) {
      View parent = v.getParent();
      v.setParent(this);
      super.forwardUpdateToView(v, e, a, f);
      v.setParent(parent);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void forwardUpdate(DocumentEvent.ElementChange ec,
        DocumentEvent e, Shape a, ViewFactory f) {
      // Update the view responsible for the changed element by invocation of
      // super method.
      super.forwardUpdate(ec, e, a, f);
      // Re-calculate the update indexes and update the views followed by
      // the changed place. Note: we update the views only when insertion or
      // removal takes place.
      DocumentEvent.EventType type = e.getType();
      if (type == DocumentEvent.EventType.INSERT ||
          type == DocumentEvent.EventType.REMOVE) {
        firstUpdateIndex = Math.min((lastUpdateIndex + 1), (getViewCount() - 1));
        lastUpdateIndex = Math.max((getViewCount() - 1), 0);
        for (int i = firstUpdateIndex; i <= lastUpdateIndex; i++) {
          View v = getView(i);
          if (v != null) {
            v.updateAfterChange();
          }
        }
      }
    }

    // The following methods don't do anything useful, they
    // simply keep the class from being abstract.

    /**
     * Renders using the given rendering surface and area on that
     * surface.  This is implemented to do nothing, the logical
     * view is never visible.
     *
     * @param g the rendering surface to use
     * @param allocation the allocated region to render into
     * @see View#paint
     */
    public void paint(Graphics g, Shape allocation) {
    }

    /**
     * Tests whether a point lies before the rectangle range.
     * Implemented to return false, as hit detection is not
     * performed on the logical view.
     *
     * @param x the X coordinate &gt;= 0
     * @param y the Y coordinate &gt;= 0
     * @param alloc the rectangle
     * @return true if the point is before the specified range
     */
    protected boolean isBefore(int x, int y, Rectangle alloc) {
      return false;
    }

    /**
     * Tests whether a point lies after the rectangle range.
     * Implemented to return false, as hit detection is not
     * performed on the logical view.
     *
     * @param x the X coordinate &gt;= 0
     * @param y the Y coordinate &gt;= 0
     * @param alloc the rectangle
     * @return true if the point is after the specified range
     */
    protected boolean isAfter(int x, int y, Rectangle alloc) {
      return false;
    }

    /**
     * Fetches the child view at the given point.
     * Implemented to return null, as hit detection is not
     * performed on the logical view.
     *
     * @param x the X coordinate &gt;= 0
     * @param y the Y coordinate &gt;= 0
     * @param alloc the parent's allocation on entry, which should be changed to the child's
     * allocation on exit
     * @return the child view
     */
    protected View getViewAtPoint(int x, int y, Rectangle alloc) {
      return null;
    }

    /**
     * Returns the allocation for a given child.
     * Implemented to do nothing, as the logical view doesn't
     * perform layout on the children.
     *
     * @param index the index of the child, &gt;= 0 &amp;&amp; &lt; getViewCount()
     * @param a the allocation to the interior of the box on entry, and the allocation of the child
     * view at the index on exit.
     */
    protected void childAllocation(int index, Rectangle a) {
    }
  }


}
